/*!
\copyright  Copyright (c) 2023 - 2024 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file       common_test.c
\brief      Implementation of common testing functions.
*/

#include "common_test.h"

#include <logging.h>
#include <connection_manager.h>
#include <ctype.h>
#include <device_list.h>
#include <device_db_serialiser.h>
#include <vm.h>
#include "hfp_profile.h"
#include "hfp_profile_instance.h"
#include "hfp_profile_typedef.h"
#include "kymera_output_common_chain.h"
#include "handset_service.h"
#include <av.h>
#include "le_advertising_manager.h"
#include "ui.h"
#ifdef INCLUDE_FAST_PAIR
#include <fast_pair.h>
#include <user_accounts.h>
#endif
#include <focus_plugin.h>
#include <focus_select_config_types.h>
#ifdef INCLUDE_GOOGLE_FINDER
#include "google_finder.h"
#endif
#include "device_properties.h"
#include "profile_manager.h"
#ifdef INCLUDE_GATT_QSS_SERVER
#include "gatt_server_qss.h"
#endif
#include "context_framework.h"
#include <pairing.h>
#include <multidevice.h>

UNITCODESECTION(KEEP)
bool appTestIsHandsetQhsConnectedAddr(const bdaddr* handset_bd_addr)
{
    bool qhs_connected_status = FALSE;

    if (handset_bd_addr != NULL)
    {
        qhs_connected_status = ConManagerGetQhsConnectStatus(handset_bd_addr);

        DEBUG_LOG_ALWAYS("appTestIsHandsetQhsConnectedAddr addr [%04x,%02x,%06lx] qhs_connected:%d", 
                         handset_bd_addr->nap,
                         handset_bd_addr->uap,
                         handset_bd_addr->lap,
                         qhs_connected_status);
    }
    else
    {
        DEBUG_LOG_WARN("appTestIsHandsetQhsConnectedAddr BT adrress is NULL");
    }

    return qhs_connected_status;
}

UNITCODESECTION(KEEP)
bool appTestIsHandsetAddrConnected(const bdaddr* handset_bd_addr)
{
    bool is_connected = FALSE;

    if (handset_bd_addr != NULL)
    {
        device_t device = BtDevice_GetDeviceForBdAddr(handset_bd_addr);
        if (device != NULL)
        {
            uint32 connected_profiles = BtDevice_GetConnectedProfiles(device);
            if ((connected_profiles & (DEVICE_PROFILE_HFP | DEVICE_PROFILE_A2DP | DEVICE_PROFILE_AVRCP)) != 0)
            {
                is_connected = TRUE;
            }
        }

        DEBUG_LOG_ALWAYS("appTestIsHandsetAddrConnected addr [%04x,%02x,%06lx] device:%p is_connected:%d",
                         handset_bd_addr->nap,
                         handset_bd_addr->uap,
                         handset_bd_addr->lap,
                         device,
                         is_connected);
    }
    else
    {
        DEBUG_LOG_WARN("appTestIsHandsetAddrConnected BT address is NULL");
    }

    return is_connected;
}

UNITCODESECTION(KEEP)
bool appTestIsHandsetAddrConnectedOverLE(const bdaddr* handset_bd_addr)
{
    bool is_connected = FALSE;

    if (handset_bd_addr != NULL)
    {
        device_t device = BtDevice_GetDeviceForBdAddr(handset_bd_addr);
        if (device != NULL)
        {
            tp_bdaddr handset_tp_bdaddr = {
                .transport = TRANSPORT_BLE_ACL,
                .taddr = {
                    .type = TYPED_BDADDR_PUBLIC,
                    .addr = {
                        .nap = handset_bd_addr->nap,
                        .uap = handset_bd_addr->uap,
                        .lap = handset_bd_addr->lap
                    }
                }
            };

            is_connected = ConManagerIsTpConnected(&handset_tp_bdaddr);
        }

        DEBUG_LOG_ALWAYS("appTestIsHandsetAddrConnectedOverLE addr [%04x,%02x,%06lx] device:%p is_connected:%d",
                         handset_bd_addr->nap,
                         handset_bd_addr->uap,
                         handset_bd_addr->lap,
                         device,
                         is_connected);
    }
    else
    {
        DEBUG_LOG_WARN("appTestIsHandsetAddrConnectedOverLE BT address is NULL");
    }

    return is_connected;
}

UNITCODESECTION(KEEP)
bool appTestIsHandsetHfpScoActiveAddr(const bdaddr* handset_bd_addr)
{
    bool is_sco_active = FALSE;

    if (handset_bd_addr != NULL)
    {
        hfpInstanceTaskData* instance = HfpProfileInstance_GetInstanceForBdaddr(handset_bd_addr);

        is_sco_active = HfpProfile_IsScoActiveForInstance(instance);
        DEBUG_LOG_ALWAYS("appTestIsHandsetHfpScoActiveAddr addr [%04x,%02x,%06lx] is_sco_active:%d", 
                         handset_bd_addr->nap,
                         handset_bd_addr->uap,
                         handset_bd_addr->lap,
                         is_sco_active);
    }
    else
    {
        DEBUG_LOG_WARN("appTestIsHandsetHfpScoActiveAddr BT adrress is NULL");
    }

    return is_sco_active;
}

UNITCODESECTION(KEEP)
void appTestEnableCommonChain(void)
{
    DEBUG_LOG_ALWAYS("appTestEnableCommonChain");
    Kymera_OutputCommonChainEnable();
}

UNITCODESECTION(KEEP)
void appTestDisableCommonChain(void)
{
    DEBUG_LOG_ALWAYS("appTestDisableCommonChain");
    Kymera_OutputCommonChainDisable();
}

UNITCODESECTION(KEEP)
int16 appTestGetRssiOfTpAddr(tp_bdaddr *tpaddr)
{
    int16 rssi = 0;
    if(VmBdAddrGetRssi(tpaddr, &rssi) == FALSE)
    {
        rssi = 0;
    }
    DEBUG_LOG_ALWAYS("appTestGetRssiOfConnectedTpAddr transport=%d tpaddr=%04x,%02x,%06lx RSSI=%d",
                    tpaddr->transport, tpaddr->taddr.addr.lap, tpaddr->taddr.addr.uap, tpaddr->taddr.addr.nap, rssi);
    return rssi;
}

UNITCODESECTION(KEEP)
int16 appTestGetBredrRssiOfConnectedHandset(void)
{
    tp_bdaddr tp_addr;
    if(HandsetService_GetConnectedBredrHandsetTpAddress(&tp_addr))
    {
        return appTestGetRssiOfTpAddr(&tp_addr);
    }
    return 0;
}

UNITCODESECTION(KEEP)
int16 appTestGetLeRssiOfConnectedHandset(void)
{
    tp_bdaddr tp_addr;
    if(HandsetService_GetConnectedLeHandsetTpAddress(&tp_addr))
    {
        return appTestGetRssiOfTpAddr(&tp_addr);
    }
    return 0;
}

UNITCODESECTION(KEEP)
bool appTestEnableConnectionBargeIn(void)
{
    /* Connection barge-in is not compatible with the legacy version. 
       Attempts to enable connection barge-in when MULTIPOINT_BARGE_IN_ENABLED
       will fail. MULTIPOINT_BARGE_IN_ENABLED should only be used where truncated 
       page scan is not supported. */
    return HandsetService_SetConnectionBargeInEnable(TRUE);
}

UNITCODESECTION(KEEP)
bool appTestDisableConnectionBargeIn(void)
{
    return HandsetService_SetConnectionBargeInEnable(FALSE);
}

UNITCODESECTION(KEEP)
bool appTestIsConnectionBargeInEnabled(void)
{
    return HandsetService_IsConnectionBargeInEnabled();
}

/*! \brief Return if Earbud/Headset is in A2DP streaming mode with any connected handset
*/
UNITCODESECTION(KEEP)
bool appTestIsHandsetA2dpStreaming(void)
{
    bool streaming = FALSE;
    bdaddr* bd_addr = NULL;
    unsigned num_addresses = 0;

    if (BtDevice_GetAllHandsetBdAddr(&bd_addr, &num_addresses))
    {
        unsigned index;
        for(index = 0; index < num_addresses; index++)
        {
            if(appTestIsHandsetA2dpStreamingBdaddr(&bd_addr[index]))
            {
                streaming = TRUE;
                break;
            }
        }
        free(bd_addr);
        bd_addr  = NULL;
    }

    DEBUG_LOG_ALWAYS("appTestIsHandsetA2dpStreaming:%d", streaming);
    return streaming;
}

/*! \brief Return if Earbud/Headset is in A2DP streaming mode with a handset having specified bluetooth address
*/
UNITCODESECTION(KEEP)
bool appTestIsHandsetA2dpStreamingBdaddr(bdaddr* bd_addr)
{
    bool streaming = FALSE;

    /* Find handset AV instance */
    avInstanceTaskData *theInst = appAvInstanceFindFromBdAddr(bd_addr);
    streaming = theInst && appA2dpIsStreaming(theInst);

    return streaming;
}

UNITCODESECTION(KEEP)
void appTestConfigureLinkLossReconnectionParameters(
        bool use_unlimited_reconnection_attempts,
        uint8 num_connection_attempts,
        uint32 reconnection_page_interval_ms,
        uint16 reconnection_page_timeout_ms)
{
    DEBUG_LOG_ALWAYS("appTestConfigureLinkLossReconnectionParameters use_unlimited_reconnection_attempts=%d "
                     "num_connection_attempts=%d reconnection_page_interval_ms=%d reconnection_page_timeout_ms=%d",
                     use_unlimited_reconnection_attempts, num_connection_attempts,
                     reconnection_page_interval_ms, reconnection_page_timeout_ms);

    HandsetService_ConfigureLinkLossReconnectionParameters(
                use_unlimited_reconnection_attempts,
                num_connection_attempts,
                HandsetService_GetInitialReconnectionPageInterval(),
                reconnection_page_timeout_ms,
                reconnection_page_interval_ms,
                reconnection_page_timeout_ms);
}

UNITCODESECTION(KEEP)
void appTestConfigureRecommendedInfiniteLinkLossReconnectionParameters(bool use_unlimited_reconnection_attempts)
{
    DEBUG_LOG_ALWAYS("appTestConfigureRecommendedInfiniteLinkLossReconnectionParameters enabled=%d",
                     use_unlimited_reconnection_attempts);

    HandsetService_ConfigureLinkLossReconnectionParameters(use_unlimited_reconnection_attempts, 3, 500, 5000, 30000, 5000);
}

/*! \brief Suspend the media stream
    Added to support PTS qualification TCs.
*/
UNITCODESECTION(KEEP)
bool appTestHandsetA2dpMediaSuspendAllowRemoteStart(void)
{
    DEBUG_LOG_ALWAYS("appTestHandsetA2dpMediaSuspendAllowRemoteStart");
    bdaddr bd_addr;
    if (appDeviceGetHandsetBdAddr(&bd_addr))
    {
        /* Find handset AV instance */
        avInstanceTaskData *theInst = appAvInstanceFindFromBdAddr(&bd_addr);
        if (theInst)
        {
            /* Suspending the stream using this sets the suspend reason as AV_SUSPEND_REASON_REMOTE.
             * To resume a stream remotely (which is done by PTS for GAVDP/INT/APP/TRC/BV-02-C test),
             * it needs to be suspended remotely. Hence, we use this function.
             */
            A2dpProfile_SuspendMedia(theInst);
            return TRUE;
        }
    }
    return FALSE;
}

#ifdef INCLUDE_FAST_PAIR
UNITCODESECTION(KEEP)
void appTestAssignFastPairAccountKeyToAllHandsets(void)
{
    for (unsigned idx = 0; idx < DeviceList_GetMaxTrustedDevices(); idx++)
    {
        device_t device = DeviceList_GetDeviceAtIndex(idx);

        if (device && BtDevice_GetDeviceType(device) == DEVICE_TYPE_HANDSET)
        {
            tp_bdaddr tpbdaddr = { .taddr.addr = DeviceProperties_GetBdAddr(device) };
            uint8 hs_key_index = UserAccounts_GetAccountKeyIndexWithHandset(device);

            /* Ignore existing handsets which already have bound account keys */
            if (hs_key_index != INVALID_USER_ACCOUNT_KEY_INDEX)
            {
                DEBUG_LOG_INFO("appTestAssignFastPairAccountKeyToAllHandsets, existing, lap=%06lx, aidx=%u", tpbdaddr.taddr.addr.lap, hs_key_index);
                continue;
            }

            /* Generate a new account key and bind it to this handset */
            uint8 account_key[FAST_PAIR_ACCOUNT_KEY_LEN];
            memset(account_key, 0xfe, sizeof(account_key));

            /* First byte must be 0x4, per GFP rules */
            account_key[0] = 0x04;
            account_key[FAST_PAIR_ACCOUNT_KEY_LEN - 1] = UtilRandom() & 0xff;

            hs_key_index = UserAccounts_AddAccountKeyForHandset(&tpbdaddr, account_key, sizeof(account_key), user_account_type_fast_pair);
            PanicFalse(hs_key_index != INVALID_USER_ACCOUNT_KEY_INDEX);

            DEBUG_LOG_INFO("appTestAssignFastPairAccountKeyToAllHandsets, new, lap=%06lx, aidx=%u", tpbdaddr.taddr.addr.lap, hs_key_index);
            DEBUG_LOG_DATA_INFO(account_key, sizeof(account_key));
        }
    }
}

UNITCODESECTION(KEEP)
void appTest_SassDisableConnectionSwitch(void)
{
    DEBUG_LOG_ALWAYS("appTest_SassDisableConnectionSwitch");
    Ui_InjectUiInput(ui_input_disable_sass_connection_switch);
}

UNITCODESECTION(KEEP)
void appTest_SassEnableConnectionSwitch(void)
{
    DEBUG_LOG_ALWAYS("appTest_SassEnableConnectionSwitch");
    Ui_InjectUiInput(ui_input_enable_sass_connection_switch);
}

UNITCODESECTION(KEEP)
bool appTest_IsSassSwitchDisabled(void)
{
    bool disabled = Sass_IsConnectionSwitchDisabled();
    DEBUG_LOG_ALWAYS("appTest_IsSassSwitchDisabled:%d",disabled);
    return disabled;
}

void appTestFastPairSetMaxAdvertisingTxPower(int8 max_tx_power)
{
    FastPair_SetMaxAdvertisingTxPower(max_tx_power);
}

#endif /* INCLUDE_FAST_PAIR */

UNITCODESECTION(KEEP)
bool appTestBlockDisconnectOfHandsetsWithDfuConnection(bool block)
{
    return Focus_SetConfig(focus_select_block_disconnect_with_dfu, &block);
}

UNITCODESECTION(KEEP)
bool appTestBlockDisconnectOfHandsetsWithVoiceOrAudioFocus(bool block)
{
    return Focus_SetConfig(focus_select_block_disconnect_with_audio, &block);
}

UNITCODESECTION(KEEP)
bool appTestCancelAptxVoicePacketsCounter(const bdaddr* handset_bd_addr)
{
    bool disabled =  FALSE;

    if (handset_bd_addr != NULL)
    {
       disabled = HfpProfileInstance_DisableAptxBlacklistTimer(handset_bd_addr);
    }

    return disabled;
}

#ifdef INCLUDE_GOOGLE_FINDER
UNITCODESECTION(KEEP)
void appTest_GoogleFinderEnableLocationTracking(void)
{
    DEBUG_LOG_ALWAYS("appTest_GoogleFinderEnableLocationTracking");
    Ui_InjectUiInput(ui_input_enable_google_finder_location_tracking);
}

UNITCODESECTION(KEEP)
void appTest_GoogleFinderDisableLocationTracking(void)
{
    DEBUG_LOG_ALWAYS("appTest_GoogleFinderDisableLocationTracking");
    Ui_InjectUiInput(ui_input_disable_google_finder_location_tracking);
}

UNITCODESECTION(KEEP)
bool appTest_IsGoogleFinderTrackingDisabled(void)
{
    bool disabled = GoogleFinder_IsLocationTrackingDisabled();

    DEBUG_LOG_ALWAYS("appTest_IsGoogleFinderTrackingDisabled - %d", disabled);
    return disabled;
}
#endif

UNITCODESECTION(KEEP)
#ifdef INCLUDE_GATT_QSS_SERVER
bool appTest_SetQssUserDescription(const char *description, uint8 len)
{
    bool status = GattServerQss_SetQssUserDescription(description, len);

    DEBUG_LOG_ALWAYS("appTest_SetQssUserDescription, status: %d", status);
    return status;
}
#endif

UNITCODESECTION(KEEP)
static void appTest_ReportProperty(device_t device, device_property_t property)
{
    if (Device_IsPropertySet(device,property))
    {
        uint8 value = 0;
        uint16 u16_value = 0;
        uint32 u32_value = 0;
        switch (property)
        {
            case device_property_bdaddr:
                {
                    bdaddr bd_addr = DeviceProperties_GetBdAddr(device);
                    DEBUG_LOG_VERBOSE("\tbdaddr nap %x uap %x lap %x", bd_addr.nap, bd_addr.uap, bd_addr.lap);
                }
                break;
            case device_property_audio_volume:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\taudio_volume %02x", value);
                break;
            case device_property_audio_source:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\taudio_source %02x", value);
                break;
            case device_property_av_instance:
                DEBUG_LOG_VERBOSE("\tav_instance %08x", Av_InstanceFindFromDevice(device));
                break;
            case device_property_hfp_profile:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\thfp_profile %02x", value);
                break;
            case device_property_type:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\ttype %02x", value);
                break;
            case device_property_link_mode:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tlink_mode %02x", value);
                break;
            case device_property_supported_profiles:
                Device_GetPropertyU32(device, property, &u32_value);
                DEBUG_LOG_VERBOSE("\tsupported_profiles %08x", u32_value);
                break;
            case device_property_connected_profiles:
                Device_GetPropertyU32(device, property, &u32_value);
                DEBUG_LOG_VERBOSE("\tcurrently connected_profiles %08x", u32_value);
                break;
            case device_property_flags:
                Device_GetPropertyU16(device, property, &u16_value);
                DEBUG_LOG_VERBOSE("\tflags %04x", u16_value);
                break;
            case device_property_sco_fwd_features:
                Device_GetPropertyU16(device, property, &u16_value);
                DEBUG_LOG_VERBOSE("\tsco_fwd_features %04x", u16_value);
                break;
            case device_property_mru:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tmru %1x", value);
                break;
            case device_property_profile_request_index:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tprofile_request_index %1x", value);
                break;
            case device_property_profiles_connect_order:
                {
                    profile_t *ptr_profile_order = NULL;
                    size_t size;
                    PanicFalse(Device_GetProperty(device, property, (void *)&ptr_profile_order, &size));
                    profile_t array[5] = {profile_manager_bad_profile};
                    memcpy(array, ptr_profile_order, MIN(size,5));
                    DEBUG_LOG_VERBOSE("\tprofiles_connect_order %d,%d,%d,%d,%d", array[0], array[1], array[2], array[3], array[4]);
                }
                break;
            case device_property_voice_volume:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tvoice_volume %02x", value);
                break;
            case device_property_va_flags:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tva_flags %02x", value);
                break;
            case device_property_va_locale:
                {
                    uint8 va_locale[4] = { '#','#','#','#' };
                    void *ptr;
                    size_t size;
                    if( Device_GetProperty(device, property, &ptr, &size) )
                    {
                        memcpy(va_locale, ptr, MIN(size,sizeof(va_locale)));
                        for ( size_t i = 0 ; i < sizeof(va_locale) ; i++ )
                        {
                            if ( va_locale[i] == '\0' ) va_locale[i] = '.';
                            if ( !isprint(va_locale[i]) ) va_locale[i] = '?';
                        }
                    }
                    DEBUG_LOG_VERBOSE("\tva_locale \"%c%c%c%c\"",
                                    va_locale[0], va_locale[1], va_locale[2], va_locale[3]);
                }
                break;
            case device_property_voice_assistant:
                Device_GetPropertyU8(device, property, &value);
                DEBUG_LOG_VERBOSE("\tvoice_assistant %02x", value);
                break;
            default:
            {
                void * property_value = NULL;
                size_t property_size = 0;
                Device_GetProperty(device, property, &property_value, &property_size);
                DEBUG_LOG_VERBOSE("\tenum:earbud_device_property_t:%d", property);
                DEBUG_LOG_DATA_VERBOSE(property_value, property_size);
                break;
            }
        }
    }
}

UNITCODESECTION(KEEP)
static void inline appTest_ReportDevicePropertyRange(device_t device, int start_index, int end_index)
{
    for (int property=start_index; property < end_index; property++)
    {
        if (Device_IsPropertySet(device, property))
        {
            appTest_ReportProperty(device, property);
        }
    }
}

UNITCODESECTION(KEEP)
static void appTest_ReportDeviceData(device_t device, void * data)
{
    UNUSED(data);

    DEBUG_LOG_ALWAYS("Device %x", device);
    appTest_ReportDevicePropertyRange(device, 0, device_property_max_num);
    appTest_ReportDevicePropertyRange(device, device_property_customer_specific_properties_start, device_property_customer_specific_properties_max);
}

UNITCODESECTION(KEEP)
void appTest_DeviceDatabaseReport(void)
{
    DEBUG_LOG_ALWAYS("DeviceDatabase");
    DeviceList_Iterate(appTest_ReportDeviceData, NULL);
}

static context_streaming_info_t streaming_info;

UNITCODESECTION(KEEP)

bool appTestGetStreamingInfo(void)
{
    if (ContextFramework_GetContextItem(context_streaming_info, (unsigned *)&streaming_info, sizeof (context_streaming_info_t)))
    {
        DEBUG_LOG_ALWAYS("appTestGetStreamingInfo:  pri rssi %d, bitrate %d, is aptx adaptive %d, is aptx lossless %d", streaming_info.common.primary_rssi, streaming_info.common.bitrate, streaming_info.common.is_adaptive, streaming_info.common.is_lossless);
        return TRUE;
    }

    return FALSE;
}

bool appTest_SetConsolidatedHandsetCountEnable(bool enabled)
{
    bool status = FALSE;

    status = HandsetService_SetConsolidatedHandsetCountEnable(enabled);
    DEBUG_LOG_ALWAYS("appTest_SetConsolidatedHandsetCountEnable: %u", status);

    return status;
}

bool appTest_ConsolidatedHandsetCountEnabled(void)
{
    bool enabled = FALSE;

    enabled = HandsetService_ConsolidatedHandsetCountEnabled();
    DEBUG_LOG_ALWAYS("appTest_ConsolidatedHandsetCountEnabled: %u", enabled);

    return enabled;
}

UNITCODESECTION(KEEP)
static common_test_sco_mode_t commonTest_convertHfScoCodecpToTestApiType(hfp_sco_codec_mode_t codec)
{
    switch(codec)
    {
    case hfp_sco_codec_mode_none:
        return sco_mode_none;
    case hfp_sco_codec_mode_narrowband:
        return sco_mode_narrowband;
    case hfp_sco_codec_mode_wideband:
        return sco_mode_wideband;
    case hfp_sco_codec_mode_ultra_wideband:
        return sco_mode_ultra_wideband;
    case hfp_sco_codec_mode_super_wideband:
        return sco_mode_super_wideband;
#ifdef INCLUDE_SWB_LC3
    case hfp_sco_codec_mode_super_wideband_lc3:
        return sco_mode_super_wideband_lc3;
#endif
    default:
        Panic();
        return sco_mode_none;
    }
}

UNITCODESECTION(KEEP)
common_test_sco_mode_t appTestGetHfpCodec(void)
{
    common_test_sco_mode_t sco_mode = commonTest_convertHfScoCodecpToTestApiType(HfpProfile_GetActiveScoCodecMode());
    DEBUG_LOG("appTestGetHfpCodec enum:common_test_sco_mode_t:%d", sco_mode);
    return sco_mode;
}

/*! \brief Put Headset into Handset Pairing mode
*/
UNITCODESECTION(KEEP)
void appTestPairHandset(void)
{
    DEBUG_LOG_ALWAYS("appTestPairHandset");
    Ui_InjectUiInput(ui_input_sm_pair_handset);
}

/*! \brief Return if Headset is in a Pairing mode
*/
UNITCODESECTION(KEEP)
bool appTestIsPairingInProgress(void)
{
    bool pip = !PairingIsIdle();

    DEBUG_LOG_ALWAYS("appTestIsPairingInProgress:%d", pip);

    return pip;
}

/*! \brief Return if Headset has a handset paired
 */
UNITCODESECTION(KEEP)
bool appTestIsHandsetPaired(void)
{
    bool paired = BtDevice_IsPairedWithHandset();

    DEBUG_LOG_ALWAYS("appTestIsHandsetPaired:%d",paired);

    return paired;
}

/*! \brief Delete all Handset pairing
*/
UNITCODESECTION(KEEP)
void appTestDeleteHandset(void)
{
    DEBUG_LOG_ALWAYS("appTestDeleteHandset");
    Ui_InjectUiInput(ui_input_sm_delete_handsets);
}

/*! \brief Delete all Handset pairing
*/
UNITCODESECTION(KEEP)
void appTestDeleteAllHandsets(void)
{
    DEBUG_LOG_ALWAYS("appTestDeleteAllHandsets");
    Ui_InjectUiInput(ui_input_sm_delete_handsets);
}

UNITCODESECTION(KEEP)
void appTestDeleteAddr(bdaddr *addr)
{
    device_t device = BtDevice_GetDeviceForBdAddr(addr);
    BtDevice_SafeDelete(device);
}

UNITCODESECTION(KEEP)
uint8 appTestGenerateHandsets(uint8 num_handsets, bool bredr_properties, bool lea_properties)
{
    DEBUG_LOG_ALWAYS("appTestGenerateHandsets, count=%u, bredr=%u, lea=%u", num_handsets, bredr_properties, lea_properties);

    /* Allow this to get called only on the Primary,
     * so that all the new device properties get correctly synced. */
    if (Multidevice_IsPair() && !BtDevice_IsMyAddressPrimary())
    {
        DEBUG_LOG_ERROR("appTestGenerateHandsets, invalid role");
        return 0;
    }

    for (unsigned idx = 0; idx < num_handsets; idx++)
    {
        /* Generate random BD_ADDR and link keys */
        uint32 random_lap = UtilRandom() | 0xF00000;

        const bdaddr bd_addr = {.nap = 0x0002, .uap = 0x5D, .lap = random_lap};

#define TEST_DUMMY_LINK_KEYS_128B       0x0123, 0x4567, 0x89AB, 0xCDEF, 0x0123, 0x4567, 0x89AB, 0xCDEF

        PAIRING_LONG_TERM_KEY_T bredr_lk = {TEST_DUMMY_LINK_KEYS_128B};
        PAIRING_LONG_TERM_KEY_T le_ltk = {TEST_DUMMY_LINK_KEYS_128B};
        PAIRING_LONG_TERM_KEY_T le_irk = {TEST_DUMMY_LINK_KEYS_128B};

        /* For earbuds, this marks each new handset (record) for forwarding to the peer.
         * To initiate the forwarding of both records and properties, the tester should next call KeySync_Sync()
         * on the Primary or reboot the Secondary. */

        uint16 flags = DEVICE_FLAGS_HANDSET_ADDRESS_FORWARD_REQD;

        device_t device = Pairing_AddBondedDevice(&bd_addr, DEVICE_TYPE_HANDSET, flags, &bredr_lk, &le_ltk, &le_irk);

        if (device == NULL)
        {
            DEBUG_LOG_ERROR("appTestGenerateHandsets, lap=0x%06lx, bond creation failed", bd_addr.lap);
            return idx;
        }

        DEBUG_LOG_ALWAYS("appTestGenerateHandsets, lap=0x%06lx, added device=%p", bd_addr.lap, device);

        /* Populate the device's properties, especially the ones that are sync-worthy.
         * The values should be harmless defaults, which will never be used by the application,
         * since there will never be connections with these handsets. */

        if (bredr_properties)
        {
            DeviceProperties_SetHandsetBredrContext(device, handset_bredr_context_not_available);
        }

        if (lea_properties)
        {
            uint8 dummy_data[8] = { 0 };
            Device_SetProperty(device, device_property_media_control_client, dummy_data, sizeof(dummy_data));
            Device_SetProperty(device, device_property_call_control_client, dummy_data, sizeof(dummy_data));
            Device_SetProperty(device, device_property_tmap_client, dummy_data, sizeof(dummy_data));
            Device_SetProperty(device, device_property_le_audio_unicast_config, dummy_data, sizeof(dummy_data));
            Device_SetPropertyU8(device, device_property_le_audio_micp_config, 0);
        }

        if (bredr_properties || lea_properties)
        {
            /* Add common LE GATT properties */
            appDeviceSetBatterServerConfigLeft(&bd_addr, 0);
            appDeviceSetBatterServerConfigRight(&bd_addr, 0);
            appDeviceSetGattServerConfig(&bd_addr, 0);
        }
    }

    DeviceDbSerialiser_Serialise();
    return num_handsets;
}

/*! \brief Send A2dp Media Resume request to remote
*/
UNITCODESECTION(KEEP)
bool appTestHandsetA2dpMediaResume(void)
{
    bdaddr bd_addr;

    DEBUG_LOG_ALWAYS("appTestHandsetA2dpMediaResume");

    if (appDeviceGetHandsetBdAddr(&bd_addr))
    {
        /* Find handset AV instance */
        avInstanceTaskData *theInst = appAvInstanceFindFromBdAddr(&bd_addr);

        if (theInst)
        {
#ifdef USE_SYNERGY
            A2dpProfile_ResumeMedia(theInst);
            return TRUE;
#else
            return A2dpMediaStartRequest(theInst->a2dp.device_id, theInst->a2dp.stream_id);
#endif
        }
    }

    return FALSE;
}

/*! \brief To Restart pairing timeout
 *  Return FALSE if it fail to restart, TRUE if it success
*/
UNITCODESECTION(KEEP)
bool appTestHandsetRestartPairingTimeout(void)
{
    bool status;
    status = HandsetService_RestartPairingTimeout();
    DEBUG_LOG_ALWAYS("appTestHandsetRestartPairingTimeout: %u", status);
    return status;
}
